/*
CpGears v1.0

Copyright (c) 2010 Yves Riel http://www.flash-factor.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

package cpgears {
	
	import cpgears.cpgearsInternal;
	import cpgears.helpers.CaptivateHelper;
	import cpgears.log.ILogger;
	import cpgears.log.LogManager;
	import cpgears.movie.CPMovieAdapter;
	import cpgears.movie.IMovie;
	import cpgears.properties.IWidgetProperties;
	import cpgears.properties.PropertiesDialog;
	import cpgears.properties.WidgetPropertiesBase;
	import cpgears.views.manager.EmptyViewManager;
	import cpgears.views.manager.IWidgetViewManager;
	import cpgears.WidgetMode;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.external.ExternalInterface;
	import flash.utils.Timer;

	use namespace cpgearsInternal;

	/**
	 * Base Implementation of a Captivate Widget.
	 * 
	 * <p>Instanciating this class on its own will not create a valid Captivate widget.</p>
	 * 
	 * @author Whyves
	 * 
	 * @see cpgears.StaticWidget
	 * 
	 */
	public class WidgetBase extends MovieClip{
		
		private var mMode:WidgetMode = WidgetMode.INITIALIZING;
		private var mProperties:IWidgetProperties = null;
		private var mPropertiesDialog:PropertiesDialog = null;
		private var mPreviousMode:WidgetMode = null;
		private var mReadyForSnapshot:Boolean = true;
		private var mMovie:IMovie = null;
		private var mViewManager:IWidgetViewManager;
		private var mAvatar:Sprite = null;

		private static var mLogger:ILogger = null;
		private static const mModeTimer:Timer = new Timer(100);
		
		/**
		 * Creates a Base implementation of a Captivate Widget.
		 * 
		 * @param	widgetProperties The property object that will be passed to Captivate to store the widget parameters.
		 * @param	widgetViewManager A manager that will handle which view to display during a specific widget phase.
		 */
		public function WidgetBase(widgetProperties:IWidgetProperties = null, widgetViewManager:IWidgetViewManager = null) {
			mLogger = LogManager.getLogger(this);
			mProperties = (widgetProperties == null) ? new WidgetPropertiesBase(prototype) : widgetProperties;
			mViewManager = (widgetViewManager == null) ? new EmptyViewManager() : widgetViewManager;
			mViewManager.rootContainer = this;

			mModeTimer.addEventListener(TimerEvent.TIMER, onTimerExpired);
			
			intializeInterface();
			
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			if (stage) initializeWidget();
		}
		
		/**
		 * The properties dialog parameters.
		 */
		public function get propertiesDialog():PropertiesDialog 
		{
			if (mPropertiesDialog == null) mPropertiesDialog = new PropertiesDialog();
			return mPropertiesDialog;
		}

		/**
		 * @private
		 * Callback when widget has been addded to stage
		 */
		private function onAddedToStage(event:Event):void {
			initializeWidget();
		}
		
		/**
		 * @private
		 * Initialize the widget state
		 */
		private function initializeWidget():void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);

			addChild(avatar);
			
			initialize();
			
			// By default, let put the widget in preview mode.
			mode = WidgetMode.PREVIEW;
		}
		
		/**
		 * The properties used to configure the widget.
		 */
		protected function get properties():IWidgetProperties {
			return mProperties;
		}
		
		/**
		 * Indicates if the widget is ready for a snapshot.
		 * 
		 * <p>When Captivate displays the widget on the Stage, it will interrogate it
		 * to verify if it is fully rendered before taking an image for display purpose. if
		 * the widget is fully rendered, set this property to <code>true</code>, otherwise set
		 * it to <code>false</code> and when the widget is ready, set it back to <code>true</code>.</p>
		 * 
		 * <p>Note that after a few seconds, Captivate will ignore this flag and snapshot the widget
		 * even if it is not ready.</p>
		 * 
		 */
		protected final function get readyForSnapshot():Boolean {
			return mReadyForSnapshot;
		}
		protected final function set readyForSnapshot(value:Boolean):void {
			mReadyForSnapshot = value;
		}
		
		/**
		 * The widget mode.
		 */
		protected final function get mode():WidgetMode {
			return mMode;
		}
		protected final function set mode(value:WidgetMode):void {
			mMode = value;
			mModeTimer.reset();
			mModeTimer.start();
		}
		
		/**
		 * @private
		 */
		private function onTimerExpired(event:TimerEvent):void {
			triggerModeChange();
		}

		/**
		 * @private
		 */
		private function stopModeTimer():void {
			mModeTimer.stop();
		}
		
		/**
		 * @private
		 */
		private function triggerModeChange():void {
			stopModeTimer();
			if (mPreviousMode == mode) return;
			mPreviousMode = mode;
			switch (mode) {
				case WidgetMode.EDIT:
					editProperties(properties);
					mViewManager.showEditView(properties);
					break;
				case WidgetMode.PREVIEW:
					preview(properties);
					mViewManager.showPreview(properties)
					break;
				case WidgetMode.STAGE:
					readyForSnapshot = prepareStage(properties);
					mViewManager.showStageView(properties);
					break;
				case WidgetMode.RUNTIME:
					enterRuntime(properties, mMovie);
					mViewManager.showRuntimeView(properties, mMovie);
					break;
				default:
					// Do nothing but log this
					mLogger.warn("The '{0}' mode is not recognized by the framework so it is being ignored.", mMode.value);
			}
		}
		
		//---------------------------------------------------------------------
		//
		//	Template methods 
		//
		//---------------------------------------------------------------------
		
		
		/**
		 *  Method called to initialized the widget
		 */
		protected function initialize():void {}
		
		/**
		 * Method called by the framework when Captivate wants to display the widget in the preview mode.
		 * 
		 * <p>Override this method to perform additional work</p>
		 * 
		 * @param	properties The property object passed on by Captivate
		 */
		protected function preview(properties:Object):void {
		}
		
		/**
		 * Method called by the framework when Captivate wants to display the widget in the edit mode.
		 * 
		 * <p>Override this method to perform additional work</p>
		 * 
		 * @param	properties The property object passed on by Captivate
		 */
		protected function editProperties(properties:Object):void {
		}
		
		/**
		 * Method called by the framework when Captivate wants to display the widget in the stage mode.
		 * 
		 * <p>Override this method to perform additional work</p>
		 * 
		 * @param	properties The property object passed on by Captivate
		 * @return <code>true</code> if the widget is ready to be snapshot, <code>false</code> otherwise.
		 */
		protected function prepareStage(properties:Object):Boolean {
			return true;
		}
		
		/**
		 * Method called by the framework when Captivate wants to display the widget inside the presentation.
		 * 
		 * <p>Override this method to perform additional work</p>
		 * 
		 * @param	properties The property object passed on by Captivate
		 * @param	movie The Captivate movie containing this widget
		 */
		protected function enterRuntime(properties:Object, movie:IMovie):void {
		}
		
		//---------------------------------------------------------------------
		//
		//	Captivate API methods to communicate with widgets
		//
		//---------------------------------------------------------------------

		/**
		 * @private
		 */
		private function intializeInterface():void {
			if (ExternalInterface.available) {
				CaptivateHelper.addExternalInterface("getInspectorParameters", getInspectorParameters);
				CaptivateHelper.addExternalInterface("setInspectorParameters", setInspectorParameters);
				CaptivateHelper.addExternalInterface("setParameters", setParameters);
				CaptivateHelper.addExternalInterface("cpSetValue", cpSetValue);
				CaptivateHelper.addExternalInterface("IsReadyForSnapShot", IsReadyForSnapShot);
			}
		}
		
		/**
		 *  @private
		 */
		cpgearsInternal function getInspectorParameters():Object {
			mViewManager.finalizeCurrentView();
			return properties.getPropertiesObject();
		}

		/**
		 *  @private
		 */
		cpgearsInternal function setInspectorParameters(params:Object):void {
			properties.initializeFromObject(params);
			triggerModeChange();
		}
		
		/**
		 *  @private
		 */
		cpgearsInternal function setParameters(params:Object):void {
			properties.initializeFromObject(params);
			triggerModeChange();
		}
		
		/**
		 *  @private
		 */
		public final function cpSetValue(variable:String, value:*):void {
			if (variable == "widgetMode") {
				mode = WidgetMode.getModeFromName(value);
			} else if (variable == "movieHandle" && mode != WidgetMode.RUNTIME) {
				mode = WidgetMode.RUNTIME;
				properties.initializeFromString(new XML(value.widgetParams()));
				
				mMovie = new CPMovieAdapter(value, this);
				
				triggerModeChange();
			}
		}
		/**
		 * Returns the widget's Avatar.
		 * 
		 * <p>The avatar acts as a place holder for the widget. When the widget is resized on the
		 * stage, the avatar also gets resized and allows the widget to keep its new size.</p>
		 */
		protected function get avatar():Sprite {
			if (mAvatar == null) mAvatar = createAvatar();
			return mAvatar;
		}
		
		/**
		 * @private
		 */
		private function createAvatar():Sprite {
			var myAvatar:Sprite = new Sprite();
			with (myAvatar) {
				graphics.beginFill(0xFF0000, 0);
				graphics.drawRect(0, 0, 10, 10);
				graphics.endFill();
			}
			return myAvatar;
		}
		
		/**
		 *  @private
		 */
		cpgearsInternal function IsReadyForSnapShot():Boolean {
			return readyForSnapshot;
		}
	}
}